#!/usr/bin/env python

# This file is used to generate closure.h
# Usage: closure.py > closure.h

# Parameter numbers for class method and function.
param_num = 7

###########################################################################
###   Helper functions

def makeArgList(pattern, begin, end):
    ss = []
    for i in range(begin, end):
        ss.append(pattern.replace('#', str(i)))
    return ss

def output(strlist):
    print ''.join(strlist).expandtabs(4)

###########################################################################
###   The following functions generate the closure.h body

def printHeader():
    ss = ["""// Copyright (c) 2015 Wang Yaofu.
// All right reserved.
//
// Author: Wang Yaofu voipman@qq.com
// Created: 2013/05/15
// Description:
// This file is generated with script, do not edit it directly!
// Please edit closure.py if needed.
//

#ifndef _COMMON_BASE_CLOSURE_H_
#define _COMMON_BASE_CLOSURE_H_

//#include "google/protobuf/message.h" // include google::protobuf::Closure

class ClosureA {
 public:
  ClosureA() {}
  virtual ~ClosureA() {};

  virtual void Run() = 0;
};

class ClosureBase {
public:
    virtual ~ClosureBase() {}
    virtual bool IsAutoDelete() const = 0;
};\n\n"""]

    ss.append('// This is the main Template Class. Because we want to use the same name\n')
    ss.append('// "Closure" for different type combination, we need to use template\n')
    ss.append('// specialization.\n')
    ss.append('template<\n')
    ss.append('\ttypename R')
    ss.append(''.join(
        makeArgList(',\n\ttypename A# = void',
                               begin = 1,
                               end = param_num + 2)))
    ss.append(' >')

    ss.append("""
class Closure : public ClosureBase
{
public:
    virtual R Run(\n """)

    ss.append(',\n'.join(makeArgList('\t\tA# a#', 1, param_num + 2)))

    ss.append('\n')
    ss.append('    ) = 0;\n')
    ss.append('};\n')

    ss.append("""
// The purpose of the following specialization is to be compatible with
// google::protobuf::Closure interface, the rpc stub generated by protobuf
// takes this as parameter.  The Closure template staff inherit from this
// interface, then the utilities provided following could help to create
// Closure instances for protobuf usage.
// DO NOT "using namespace google protobuf" in your code, otherwise the
// compiler will complain ambiguous definition error.
/*
template<>
class Closure<void> : public ClosureBase, public google::protobuf::Closure
{
public:
    virtual void Run() = 0;
};\n
*/
template<>
class Closure<void> : public ClosureBase, public ClosureA
{
public:
    virtual void Run() = 0;
};\n""")

    output(ss)


def printTail():
    print ''
    print '#endif // _COMMON_BASE_CLOSURE_H_'

### Generate Closure template specialization codes, for example:
"""
template<
    typename R,
    typename A1,
    typename A2 >
class Closure<R, A1, A2> : public ClosureBase
{
public:
    virtual R Run(A1, A2) = 0;
};
"""
def printClosure(num):
    ss = []
    ss.append('template<\n')
    ss.append('\ttypename R')
    ss.append(''.join(makeArgList(',\n\ttypename A#',
                                  begin = 1,
                                  end = num + 1)))

    ss.append(' >\n')
    ss.append('class Closure')
    ss.append('<R')
    ss.append(''.join(makeArgList(', A#', 1, num + 1)))

    ss.append('>')
    ss.append(' : public ClosureBase\n')
    ss.append('{\n')
    ss.append('public:\n')
    ss.append('\tvirtual R Run(')

    ss.append(', '.join(makeArgList('A#', 1, num + 1)))

    ss.append(') = 0;\n')
    ss.append('};\n')

    print "".join(ss).expandtabs(4)


def printClosures():
    for i in range(0, param_num + 1):
        printClosure(i)

def printInternalNamespaceBegin():
    print "namespace closure {\n"

def printInternalNamespaceEnd():
    print "} // namespace closure \n"

def printSelfDeleter():
    print """template<bool del, typename T>
class SelfDeleter {
public:
    SelfDeleter(T* obj)
      : mObj(obj)
    {}
    ~SelfDeleter() {
        if (del) {
            delete mObj;
        }
    }
private:
    SelfDeleter(const SelfDeleter&);
    SelfDeleter& operator=(const SelfDeleter&);
private:
    T* mObj;
};\n"""


def makeLeafClassName(total, pre_bind, classtype = 'method'):
    ss = []
    if (cmp(classtype, 'method') == 0):
        ss.append('MethodClosure')
    elif (cmp(classtype, 'function') == 0):
        ss.append('FunctionClosure')

    ss.append('_' + str(pre_bind) + '_' + str(total - pre_bind))
    return ''.join(ss)


def makeBaseClosureName(total, pre_bind):
    ss = ['Closure<R']
    ss.append(''.join(makeArgList(', A#', pre_bind + 1, total + 1)))
    ss.append('>')
    return ''.join(ss)


def printMethodClosure(total, pre_bind):
    ss = []
    # template parameter list
    ss.append('template<\n')
    ss.append('    bool del,\n')
    ss.append('    typename R,\n')
    ss.append('    typename T')

    ss.append(''.join(makeArgList(',\n\ttypename A#',
                                             begin = 1,
                                             end = total + 1)))

    ss.append(' >\n')

    # class name
    ss.append('class ' + makeLeafClassName(total, pre_bind, 'method')
            + ' : public ' + makeBaseClosureName(total, pre_bind))
    ss.append('\n')

    ss.append('{\n')

    # signature typedef
    ss.append('\ttypedef R (T::*Signature)(')
    ss.append(', '.join(makeArgList('A#', 1, total + 1)))
    ss.append(');\n')

    ss.append('public:\n')

    # ctor body
    ss.append('\t')
    ss.append(makeLeafClassName(total, pre_bind, 'method'))
    ss.append('(T* obj, Signature func')
    ss.append(''.join(makeArgList(', A# a#', 1, pre_bind + 1)))
    ss.append(')\n')
    ss.append('\t  : mObj(obj),\n')
    ss.append('\t\tmFunc(func)')

    for i in range(1, pre_bind + 1):
        ss.append(',\n')
        ss.append('\t\tmA' + str(i) + '(' + 'a' + str(i) + ')' )

    ss.append('\n')
    ss.append('\t{}\n')

    # dtor body
    ss.append('\t~' + makeLeafClassName(total, pre_bind, 'method'))
    ss.append('() {\n')
    ss.append('\t\tmObj = 0;\n')
    ss.append('\t\tmFunc = 0;\n')
    ss.append('\t}\n')

    # Run function
    ss.append('\tvirtual R Run(')
    ss.append(', '.join(makeArgList('A# a#', pre_bind + 1, total + 1)))
    ss.append(') {\n')
    ss.append('\t\tSelfDeleter<del, ClosureBase> deleter(this);\n')
    ss.append('\t\treturn (mObj->*mFunc)(')

    args = makeArgList('mA#', 1, pre_bind + 1)
    args.extend(makeArgList('a#', pre_bind + 1, total + 1))
    ss.append(', '.join(args))

    ss.append(');\n')
    ss.append('\t}\n')

    # IsAutoDelete
    ss.append('\tvirtual bool IsAutoDelete() const {\n')
    ss.append('\t\treturn del;\n')
    ss.append('\t}\n')

    # private data methods
    ss.append('private:\n')
    ss.append('\tT* mObj;\n')
    ss.append('\tSignature mFunc;\n')
    ss.append(''.join(makeArgList('\tA# mA#;\n', 1, pre_bind + 1)))

    # end of function body
    ss.append('};\n')

    output(ss)


def printMethodClosures():
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printMethodClosure(i, j)


def printFunctionClosure(total, pre_bind):
    ss = []
    # template parameter list
    ss.append('template<\n')
    ss.append('    bool del,\n')
    ss.append('    typename R')

    ss.append(''.join(makeArgList(',\n\ttypename A#',
                                  begin = 1,
                                  end = total + 1)))

    ss.append(' >\n')

    # class name
    ss.append('class ' + makeLeafClassName(total, pre_bind, 'function')
            + ' : public ' + makeBaseClosureName(total, pre_bind))
    ss.append('\n')

    ss.append('{\n')

    # signature typedef
    ss.append('\ttypedef R (*Signature)(')
    ss.append(', '.join(makeArgList('A#', 1, total + 1)))
    ss.append(');\n')

    ss.append('public:\n')

    # ctor body
    ss.append('\t')
    ss.append(makeLeafClassName(total, pre_bind, 'function'))
    ss.append('(Signature func')
    ss.append(''.join(makeArgList(', A# a#', 1, pre_bind + 1)))
    ss.append(')\n')
    ss.append('\t  : mFunc(func)')

    for i in range(1, pre_bind + 1):
        ss.append(',\n')
        ss.append('\t\tmA' + str(i) + '(' + 'a' + str(i) + ')' )

    ss.append('\n')
    ss.append('\t{}\n')

    # dtor body
    ss.append('\t~' + makeLeafClassName(total, pre_bind, 'function'))
    ss.append('() {\n')
    ss.append('\t\tmFunc = 0;\n')
    ss.append('\t}\n')

    # Run function
    ss.append('\tvirtual R Run(')
    ss.append(', '.join(makeArgList('A# a#', pre_bind + 1, total + 1)))
    ss.append(') {\n')
    ss.append('\t\tSelfDeleter<del, ClosureBase> deleter(this);\n')
    ss.append('\t\treturn (mFunc)(')

    # Parameter list, including pre-bound ones and new ones
    args = makeArgList('mA#', 1, pre_bind + 1)
    args.extend(makeArgList('a#', pre_bind + 1, total + 1))
    ss.append(', '.join(args))

    ss.append(');\n')
    ss.append('\t}\n')

    # IsAutoDelete
    ss.append('\tvirtual bool IsAutoDelete() const {\n')
    ss.append('\t\treturn del;\n')
    ss.append('\t}\n')

    # private data methods
    ss.append('private:\n')
    ss.append('\tSignature mFunc;\n')
    ss.append(''.join(makeArgList('\tA# mA#;\n', 1, pre_bind + 1)))

    # end of function body
    ss.append('};\n')

    output(ss)


def printFunctionClosures():
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printFunctionClosure(i, j)


def printNewClosure(total, pre_bind, type, permanent):
    ss = []
    if (cmp(type, 'method') == 0):
        ss.append('template<typename R, typename T')
    else:
        ss.append('template<typename R')
    ss.append(''.join(makeArgList(', typename A#', 1, total + 1)))
    ss.append('>\n')
    ss.append('Closure<R')
    ss.append(''.join(makeArgList(', A#', pre_bind + 1, total + 1)))
    if (not permanent):
        ss.append('>* NewClosure')
    else:
        ss.append('>* NewPermanentClosure')
    if (type == 'method'):
        ss.append('(T* obj, R(T::*func)(')
    else:
        ss.append('(R(*func)(')

    ss.append(', '.join(makeArgList('A#', 1, total + 1)))
    ss.append(')')
    ss.append(''.join(makeArgList(', A# a#', 1, pre_bind + 1)))
    ss.append(') {\n')
    ss.append('\treturn new closure::')
    ss.append(makeLeafClassName(total, pre_bind, type))
    if (permanent):
        ss.append('<false, R')
    else:
        ss.append('<true, R')
    if (type == 'method'):
        ss.append(', T')
        ss.append(''.join(makeArgList(', A#', 1, total + 1)))
        ss.append('>(obj, func')
    else:
        ss.append(''.join(makeArgList(', A#', 1, total + 1)))
        ss.append('>(func')

    ss.append(''.join(makeArgList(', a#', 1, pre_bind + 1)))
    ss.append(');\n')
    ss.append('}\n')

    output(ss)


def printFactoryMethods():
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printNewClosure(i, j, 'method', False)
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printNewClosure(i, j, 'method', True)
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printNewClosure(i, j, 'function', False)
    for i in range(0, param_num + 1):
        for j in range(0, i + 1):
            printNewClosure(i, j, 'function', True)


if __name__ == "__main__":
    printHeader()
    printClosures()
    printInternalNamespaceBegin()
    printSelfDeleter()
    printMethodClosures()
    printFunctionClosures()
    printInternalNamespaceEnd()
    printFactoryMethods()
    printTail()

